package zvalidation

import (
	"fmt"
	"os"
	"reflect"

	"gitee.com/youkelike/ziam/zvalidation/field"
	"github.com/gin-gonic/gin/binding"
	english "github.com/go-playground/locales/en"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	"github.com/go-playground/validator/v10/translations/en"
)

const (
	maxDescriptionLength = 255
)

type Validator struct {
	val   *validator.Validate
	data  interface{}
	trans ut.Translator
}

// 使用包变量单例
var ValidatorIns *Validator

func init() {
	ValidatorIns = NewValidator(nil)
}
func ValidateStruct(data any) field.ErrorList {
	return ValidatorIns.ValidateStruct(data)
}

// data 是已经绑定好数据、需要校验的 dto
func (v *Validator) ValidateStruct(data any) field.ErrorList {
	// 执行校验
	err := v.val.Struct(data)
	if err == nil {
		return nil
	}
	if _, ok := err.(*validator.InvalidValidationError); ok {
		return field.ErrorList{field.Invalid(field.NewPath(""), err.Error(), "")}
	}

	allErrs := field.ErrorList{}
	// 解析错误集中处理，把错误信息转换成自定义的错误类型
	vErrors, _ := err.(validator.ValidationErrors)
	for _, vErr := range vErrors {
		// 还有：vErr.Field() vErr.Value() vErr.Tag() 等
		allErrs = append(allErrs, field.Invalid(field.NewPath(vErr.Namespace()), vErr.Translate(v.trans), ""))
	}

	return allErrs
}

func NewValidator(data interface{}) *Validator {
	// 用法参考
	// https://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/%E5%8F%82%E6%95%B0%E9%AA%8C%E8%AF%81/%E5%A4%9A%E8%AF%AD%E8%A8%80%E7%BF%BB%E8%AF%91%E9%AA%8C%E8%AF%81.html
	// 实例化
	result := validator.New()

	// 注册自定义校验器
	result.RegisterValidation("dir", validateDir)                 // nolint: errcheck // no need
	result.RegisterValidation("file", validateFile)               // nolint: errcheck // no need
	result.RegisterValidation("description", validateDescription) // nolint: errcheck // no need
	result.RegisterValidation("name", validateName)               // nolint: errcheck // no need

	// 给校验器注册翻译器过程
	// 本地英文翻译器实例化
	eng := english.New()
	// 本地英文翻译器注入全局翻译器
	uni := ut.New(eng, eng)
	// 从全局翻译器得到全局英文翻译器
	trans, _ := uni.GetTranslator("en")
	// 把全局英文翻译器注册到验证器中
	err := en.RegisterDefaultTranslations(result, trans)
	if err != nil {
		panic(err)
	}

	// 添加额外翻译内容
	translations := []struct {
		tag         string
		translation string
	}{
		{
			tag:         "dir",
			translation: "{0} must point to an existing directory, but found '{1}'",
		},
		{
			tag:         "file",
			translation: "{0} must point to an existing file, but found '{1}'",
		},
		{
			tag:         "description",
			translation: fmt.Sprintf("must be less than %d", maxDescriptionLength),
		},
		{
			tag:         "name",
			translation: "is not a invalid name",
		},
	}
	for _, t := range translations {
		err = result.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation), translateFunc)
		if err != nil {
			panic(err)
		}
	}

	return &Validator{
		val:   result,
		data:  data,
		trans: trans,
	}
}

func registrationFunc(tag string, translation string) validator.RegisterTranslationsFunc {
	return func(ut ut.Translator) (err error) {
		// 定义指定 tag 校验出错时的提示语格式
		if err = ut.Add(tag, translation, true); err != nil {
			return
		}

		return
	}
}

func translateFunc(ut ut.Translator, fe validator.FieldError) string {
	// 三个参数分别是：自定义 tag 名、{0} 的填充内容、{1} 的填充内容
	t, err := ut.T(fe.Tag(), fe.Field(), reflect.ValueOf(fe.Value()).String())
	if err != nil {
		return fe.(error).Error()
	}

	return t
}

func (v *Validator) Validate() field.ErrorList {
	// 执行校验
	err := v.val.Struct(v.data)
	if err == nil {
		return nil
	}
	if _, ok := err.(*validator.InvalidValidationError); ok {
		return field.ErrorList{field.Invalid(field.NewPath(""), err.Error(), "")}
	}

	allErrs := field.ErrorList{}
	// 解析错误集中处理，把错误信息转换成自定义的错误类型
	vErrors, _ := err.(validator.ValidationErrors)
	for _, vErr := range vErrors {
		// 还有：vErr.Field() vErr.Value() vErr.Tag() 等
		allErrs = append(allErrs, field.Invalid(field.NewPath(vErr.Namespace()), vErr.Translate(v.trans), ""))
	}

	return allErrs
}

func validateDir(fl validator.FieldLevel) bool {
	// 拿到字段值
	path := fl.Field().String()
	if stat, err := os.Stat(path); err == nil && stat.IsDir() {
		return true
	}

	return false
}

func validateFile(fl validator.FieldLevel) bool {
	path := fl.Field().String()
	if stat, err := os.Stat(path); err == nil && !stat.IsDir() {
		return true
	}

	return false
}

func validateDescription(fl validator.FieldLevel) bool {
	description := fl.Field().String()

	return len(description) <= maxDescriptionLength
}

func validateName(fl validator.FieldLevel) bool {
	name := fl.Field().String()
	if errs := IsQualifiedName(name); len(errs) > 0 {
		return false
	}

	return true
}

// validateUsername checks if a given username is illegal.
func validateUsername(fl validator.FieldLevel) bool {
	username := fl.Field().String()
	if errs := IsQualifiedName(username); len(errs) > 0 {
		return false
	}

	return true
}

// validatePassword checks if a given password is illegal.
func validatePassword(fl validator.FieldLevel) bool {
	password := fl.Field().String()
	if err := IsValidPassword(password); err != nil {
		return false
	}

	return true
}

func init() {
	// 注册自定义的 binding 验证函数
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		_ = v.RegisterValidation("username", validateUsername)
		_ = v.RegisterValidation("password", validatePassword)
	}
}
